﻿using DocumentFormat.OpenXml.Lite.Core.Office;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IO = System.IO;
using System.Xml.Serialization;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.WorksheetElements;
using DocumentFormat.OpenXml.Lite.Xml;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Drawing;
using System.Xml;
using DocumentFormat.OpenXml.Lite.Core.Interfaces;

namespace DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.Base
{
    [XmlRoot("worksheet", Namespace = Constants.ExcelMain)]
    public class Worksheet : XmlSerializable, IOfficeElement, ISerializable
    {
        [XmlIgnore]
        public Guid DocumentId { get; set; }

        [Optional]
        [XmlElement("sheetFormatPr", Namespace = Constants.ExcelMain)]
        public SheetFormatProperties FormatProperties { get; set; }

        [Optional]
        [XmlElement("cols", Namespace = Constants.ExcelMain)]
        public XmlList<ColumnInformation> ColumnInformations { get; set; }

        [XmlElement("sheetData", Namespace = Constants.ExcelMain)]
        public Sheetdata Data { get; set; }

        [Optional]
        [XmlElement("mergeCells", Namespace = Constants.ExcelMain)]
        public MergeCells MergedCells { get; set; }

        [Optional]
        [XmlElement("conditionalFormatting", Namespace = Constants.ExcelMain)]
        public XmlList<ConditionalFormatting> ConditionalFormattings { get; set; }

        [Optional]
        [XmlElement("drawing")]
        public WorksheetElements.Drawing Drawing { get; set; }

        [Optional]
        [XmlElement("tableParts", Namespace = Constants.ExcelMain)]
        public TableParts TableParts { get; set; }

        [XmlIgnore]
        public string Name { get; set; }

        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces { get; set; }

        public Worksheet()
        {
            Data = new Sheetdata();
            ColumnInformations = new XmlList<ColumnInformation>();
            ConditionalFormattings = new XmlList<ConditionalFormatting>();

            Namespaces = new XmlSerializerNamespaces();
            Namespaces.Add("", Constants.ExcelMain);
            //ns.Add("xml", Constants.Xml);
        }

        public Worksheet(string name)
            : this()
        {
            this.Name = name;
        }

        public Cell CreateCell(int rowIndex, int columnIndex)
        {
            return Data.CreateCell((uint)rowIndex, (uint)columnIndex);
        }

        [XmlIgnore]
        public WorksheetDrawing DrawingElement
        {
            get
            {
                return (DocumentManager.Instance
                    .FindDocument(DocumentId) as SpreadsheetDocument)
                    .FindElementFor<WorksheetDrawing>(this, Drawing.RelationshipId);
            }
        }

        public WorksheetDrawing CreateDrawing()
        {
            var drawingElement = (DocumentManager.Instance
                    .FindDocument(DocumentId) as SpreadsheetDocument).CreateDrawingElement(this);
            this.Drawing = new WorksheetElements.Drawing()
            {
                RelationshipId = drawingElement.RelationshipId
            };
            this.Namespaces.Add("r", Constants.Relationship);
            return drawingElement;
        }

        internal TablePart AddTable(string relId)
        {
            if (TableParts == null)
            {
                TableParts = new TableParts();
                TableParts.Count = 0;
                Namespaces.Add("r", Constants.Relationship);
            }

            TablePart tp = new TablePart()
            {
                RelationshipId = relId
            };
            TableParts.Count++;
            TableParts.TablePartList.Add(tp);
            return tp;
        }

        public ColumnInformation AddColumnInformation()
        {
            if (ColumnInformations == null)
            {
                ColumnInformations = new XmlList<ColumnInformation>();
            }

            ColumnInformation inf = new ColumnInformation();
            ColumnInformations.Add(inf);
            return inf;
        }

        public void MergeCells(string from, string to)
        {
            if (MergedCells == null)
            {
                MergedCells = new MergeCells() { Count = 0 };
            }

            MergedCells.Count++;
            MergedCells.MergedCells.Add(new MergeCell()
            {
                Reference = from + ":" + to
            });
        }

        #region ISerializable members

        public byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());
            using (var stream = new IO.MemoryStream())
            {
                using (var writer = new IO.StreamWriter(stream, Encoding.UTF8))
                {
                    using (var xwriter = XmlWriter.Create(writer))
                    {
                        xwriter.WriteStartElement("", "worksheet", Constants.ExcelMain);
                        foreach (var ns in Namespaces.ToArray())
                        {
                            if (ns.Name != "")
                            {
                                xwriter.WriteAttributeString("xmlns", ns.Name, null, ns.Namespace);
                            }
                        }
                        this.WriteXml(xwriter);
                    }
                    return stream.ToArray();
                }
            }
        }

        public void Deserialize(System.IO.Stream content)
        {
            using (var reader = XmlReader.Create(content))
            {
                reader.ReadToDescendant("worksheet", Constants.ExcelMain);
                for (int i = 0; i < reader.AttributeCount; ++i)
                {
                    string s = reader.GetAttribute(i);
                    if (Constants.GetDefaultPrefix(s) != string.Empty)
                    {
                        Namespaces.Add(Constants.GetDefaultPrefix(s), s);
                    }
                }
                this.ReadXml(reader);
            }
        }

        #endregion
    }
}
